
///
/// This is the implementation of the Server class
///

#include <iostream>
#include <netdb.h>
#include <arpa/inet.h>
#include <cstring>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <cstdio>
#include <cassert>
#include "Server.h"
#include <algorithm>

Server* Server::mServer = NULL;

int Server::Initialize(const char *ip, const char *portNo)
{
	struct addrinfo temp, *res;
	memset(&temp, 0, sizeof(temp));
	temp.ai_family = AF_UNSPEC;
	temp.ai_socktype = SOCKET_TYPE;
	
	int retVal;

	if((retVal = getaddrinfo(ip, /*TFTP_PORT_STR*/portNo, &temp, &res)) != 0)
	{
		std::cout << "getaddrinfo() error: " << gai_strerror(retVal) << std::endl;
		exit(1);
	}


	std::cout << "Starting the server..." << std::endl;
	std::cout << "IP Address: " << ip << std::endl;
	std::cout << "Port Number: " << /*TFTP_PORT_STR*/portNo << std::endl;

	//create server main(connection request handler) socket
	mMainFD = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
	if(mMainFD == -1)
	{
		perror("socket() error: ");
		exit(2);
	}

	//need to reuse the socket
	int yes = 1;
	if(setsockopt(mMainFD, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) == -1)
	{
		perror("setsockopt() error: ");
		exit(2);
	}

	if(bind(mMainFD, res->ai_addr, res->ai_addrlen) == -1)
	{
		perror("bind() error: ");
		close(mMainFD);
		exit(2);
	}

	freeaddrinfo(res);

	std::cout << "Server started successfully!" << std::endl;

	struct sockaddr_in tempaddr;
	unsigned int templen = sizeof(struct sockaddr);
	getsockname(mMainFD, (struct sockaddr*)&tempaddr, &templen);
	std::cout << "Server listens to port: " << ntohs(tempaddr.sin_port) << std::endl;


	return 0;
}

int Server::Execute()
{

	struct sockaddr_storage clientAddr;
	socklen_t clientAddrSize = sizeof(clientAddr);

	//initialize FD sets for select()
	fd_set readFDS;
	fd_set checkTimeoutFDS;
	
	FD_ZERO(&checkTimeoutFDS);
	FD_ZERO(&readFDS);
	FD_ZERO(&mConnectedFDS);
	
	FD_SET(mMainFD, &mConnectedFDS);
	FD_SET(STDIN, &mConnectedFDS); //for command line control of server
	mMaxFD = std::max(STDIN, mMainFD);

	struct timeval tv;
	tv.tv_sec = 1;	tv.tv_usec = 0; // 5 min timeout

	int retVal, numBytes;
	unsigned long MxTime = 5;

	while(1)
	{
		readFDS = mConnectedFDS;
		retVal = select(mMaxFD+1, &readFDS, NULL, NULL, &tv);
		if(retVal == -1)
		{
			perror("select() error: ");
			return 3;
		}
		else if(retVal == 0)
		{
			//std::cout << "Timeout occured." << std::endl;
		}
		for(int i = 0; i <= mMaxFD; ++i)
		{
			if(FD_ISSET(i, &readFDS))
			{
				if(i == STDIN)
				{
					//TODO: handle std input commands
				}
				else if(i == mMainFD)
				{
					//seems like there's a new request
					if((numBytes = recvfrom(mMainFD, mMsgBuf, MSG_BUF_SIZE, 0, 
						(struct sockaddr*)&clientAddr, &clientAddrSize)) == -1)
					{
						perror("recvfrom() error: ");
						exit(1);
					}
					mMsgBuf[numBytes] = 0;
					std::cout << "Server received the msg:\n" << mMsgBuf
									<< std::endl;
					
					uint16_t temp, opCode;
					memcpy((char*)&temp, mMsgBuf, 2);
					opCode = ntohs(temp);
	
					assert(opCode == 1); //only RRQ supported
					std::string fileName(mMsgBuf+2);

					//thats it I guess, not to worry about the mode
					
					int clientFD = socket(PF_INET, SOCKET_TYPE, 0);
					FD_SET(clientFD, &mConnectedFDS);
					FD_SET(clientFD, &checkTimeoutFDS);
						
					timer newtimer;
					newtimer.start();
					mMaxFD = std::max(mMaxFD, clientFD);
					Handler handler(clientFD, fileName, clientAddr, 
											clientAddrSize);
					mClientMap[clientFD] = std::make_pair(newtimer, handler);
					//std::cout<<handler.finished<<std::endl;
					if (handler.finished)
					{
						FD_CLR(clientFD, &mConnectedFDS);
						FD_CLR(clientFD, &checkTimeoutFDS);
						mClientMap.erase(clientFD);
					}
				}
				else
				{
					//seems like an ACK arrived
						
					if((numBytes = recvfrom(i, mMsgBuf, MSG_BUF_SIZE, 0, 
						(struct sockaddr*)&clientAddr, &clientAddrSize)) == -1)
					{
						perror("recvfrom() error: ");
						exit(1);
					}
					mClientMap[i].second.HandleMessage(mMsgBuf, numBytes);
					
					//std::cout<<mClientMap[i].second.finished<<std::endl;
					if (mClientMap[i].second.finished)
					{
						FD_CLR(i, &mConnectedFDS);
						FD_CLR(i, &checkTimeoutFDS);
						mClientMap.erase(i);
					}
					else
					{
						mClientMap[i].first.start();
					}
						
				}
			}
		}
		for(int j = 0; j<= mMaxFD ; ++j)
                {
			
                        if(FD_ISSET(j , &checkTimeoutFDS))
                        {
				//	std::cout<<"checktimeout: "<<j<<std::endl;
                                if(mClientMap[j].first.timeout(MxTime))
                                {
					std::cout<<"Expected ACK from "<<j<<" not received, retransmitting!"<<std::endl;
                                        mClientMap[j].second.SendData();
					mClientMap[j].first.start();
                                }
                        }
                }

	}
	
	close(mMainFD);
	return 0;
}

int Server::HandleError(int sockFD)
{
	return 0;
}


#if 0
//////////////////////
//// PRIVATE /////////
//////////////////////

void Server::KillClient(int sockFD)
{
	//close the socket
	close(sockFD);

	//remove socket from fd set
	FD_CLR(sockFD, &mConnectedFDS);
	if(sockFD == mMaxFD)
	{
		UpdateMaxFd();
	}

	//remove socket from map
	mClientMap.erase(sockFD);
}

void Server::UpdateMaxFd()
{
	for(int i = mMaxFD-1; i > 0; --i)
	{
		if(FD_ISSET(i, &mConnectedFDS))
		{
			mMaxFD = i;
			return;
		}
	}
}
#endif
